<?php
/**
 * In this file the class '\UF\IO\Upload\Data' is defined.
 *
 * @category   UniKap-Framework
 * @package    IO
 * @subpackage Upload
 * @author     Ulf -UniKado- Kadner <ulfikado@gmail.com>
 * @since      2014-03-29 16:17
 * @version    0.1
 */

namespace UF\IO\Upload
{

    /**
     * This class encapsules a file upload.
     *
     * The usage is very easy:
     *
     * We suppose a field of a upload form use the name 'Image' and only files with web image file extensions allowed
     *
     * <code>
     * # Init
     * $upload = new \UF\IO\Upload\Data(
     *     # Use form field 'Image'
     *     'Image',
     *     # The max file size in bytes
     *     10485760,
     *     # Allow only files with a web image file name extension
     *     array('jpg', 'jpeg', 'gif', 'png') );
     * if ( !$upload->isUpload() )
     * {
     *     # There is currently no upload
     *     # Show the upload form
     *     showUploadForm(); # This is only a placeholder code!
     *     exit;
     * }
     * if ( $upload->hasError() )
     * {
     *     # The upload is not valid
     *     # Show the upload form with some errormessage
     *     showUploadForm( $upload->ErrorMessage ); # This is only a placeholder code!
     *     exit;
     * }
     * # Upload is valid
     * handleUpload(); # This is only a placeholder code!
     * </code>
     *
     * @since  v0.1
     */
    class Data
    {

        # <editor-fold defaultstate="collapsed" desc="- - - -   C O N S T A N T S   - - - - - - - - - - - - - - - - - - - - - - - - - -">

        /**
         * The Upload is known and valid
         *
         * @since  v0.1
         */
        const ERROR_NOERROR = UPLOAD_ERR_OK;

        /**
         * The upload fails because PHP has no rights to write in temp upload folder.
         *
         * @since  v0.1
         */
        const ERROR_WRITEFAILS = UPLOAD_ERR_CANT_WRITE;

        /**
         * The upload fails because a unallowed file name extension is used.
         *
         * @since  v0.1
         */
        const ERROR_EXTENSION = UPLOAD_ERR_EXTENSION;

        /**
         * The upload fails because file is larger than defined upload size (size defined by form)
         *
         * @since  v0.1
         */
        const ERROR_BAD_SIZE_BY_FORM = UPLOAD_ERR_FORM_SIZE;

        /**
         * The upload fails because file is larger than defined upload size (size defined by php.ini)
         *
         * @since  v0.1
         */
        const ERROR_BAD_SIZE_BY_INI = UPLOAD_ERR_INI_SIZE;

        /**
         * The upload fails because file is larger than defined upload size (size defined by class settings here)
         *
         * @since  v0.1
         */
        const ERROR_BAD_SIZE_BY_CONFIG = 20;

        /**
         * The upload fails because a bad source is used.
         *
         * @since  v0.1
         */
        const ERROR_INVALID_SOURCE = 21;

        /**
         * There is no upload or no file defined/found
         *
         * @since  v0.1
         */
        const ERROR_NO_UPLOAD = UPLOAD_ERR_NO_FILE;

        /**
         * The upload fails because no usable temp upload folder is configured for PHP.
         *
         * @since  v0.1
         */
        const ERROR_NO_TMP_FOLDER = UPLOAD_ERR_NO_TMP_DIR;

        /**
         * The upload fails because a partial error is thrown.
         *
         * @since  v0.1
         */
        const ERROR_PARTIAL = UPLOAD_ERR_PARTIAL;

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   F I E L D S   - - - - - - - - - - - - - - - - - - - - - -">

        /**
         * The temporary path of the file where the upload is located.
         *
         * @var    string
         * @since  v0.1
         */
        public  $TempFile;

        /**
         * The origin file name of the uploaded file.
         *
         * @var    string
         * @since  v0.1
         */
        public  $Filename;

        /**
         * The mimetype of the uploaded file.
         *
         * @var    string
         * @since  v0.1
         */
        public  $Mimetype;

        /**
         * The Size (in bytes) of the uploaded file.
         *
         * @var    int
         * @since  v0.1
         */
        public  $Size;

        /**
         * The datetime of the uploaded file.
         *
         * @var    \UF\DateTime
         * @since  v0.1
         */
        public  $Date;

        /**
         * The Errorcode, if upload is failed. (see {@see \UF\IO\Upload\Data}::ERROR_* constants)
         *
         * @var    int
         * @since  v0.1
         */
        public  $ErrorCode;

        /**
         * The Errormessage, depending to the $ErrorCode field.
         *
         * @var    string|null
         * @since  v0.1
         */
        public  $ErrorMessage;

        /**
         * Der Name des Upload-Formularfelds für diesen Upload.
         *
         * @var    string
         * @since  v0.1
         */
        public  $FieldName;

        /**
         * All allowed file name extensions a numeric ind. array (without the leading dot!)
         *
         * @var    array
         * @since  v0.1
         */
        public  $RequiredExtensions;

        /**
         * Defines the max size of a uploadable file in bytes.
         *
         * @var    int
         * @since  v0.1
         */
        public $MaxFileSize;

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - >   C O N S T R U C T O R   - - - - - - - - - - - - - - - - - - - - - - - -">

        /**
         * Inits a new instance.
         *
         * @param string $fieldname          The name of the $_FILES field, defining the uploaded file.
         * @param int    $maxFileSize        Maximal allowed upload file size in bytes.
         * @param array  $requiredExtensions The allowed file name extensions without the leading dots. If nothing is
         *                                   is defined here all files are allowed.
         * @param array  $uploadData         Optional array with upload data, to use as a $_FILES replacement.
         *                                   This is usefull for handling some test cases without real upload data.
         * @since  v0.1
         */
        public function __construct( $fieldname = null, $maxFileSize = 10485760,
            array $requiredExtensions = [], array $uploadData = [] )
        {
            $this->MaxFileSize = $maxFileSize;
            if ( 1 > \count( $uploadData ) && ! empty( $fieldname ) )
                if ( isset( $_FILES[$fieldname] ) ) $uploadData = $_FILES[$fieldname];
            if ( ! $this->init1( $uploadData, $requiredExtensions ) ) return;
            $this->Size = \intval( $uploadData['size'] ); $ext = null;
            if ( ! $this->init2( $uploadData, $requiredExtensions ) ) return;
            if ( ! $this->init3( $fieldname, $uploadData, $requiredExtensions, $ext ) ) return;
            $this->FieldName = $fieldname;
            $this->TempFile = $uploadData['tmp_name'];
            $this->Filename = \basename( $uploadData['name'] );
            $this->Mimetype = \UF\IO\MimetypeTool::GetByExtension( $ext );
            $this->Size = $uploadData['size'];
            $this->Date = new \UF\DateTime( filemtime( $this->TempFile ) );
            $this->ErrorCode = $uploadData['error'];
            switch ( $this->ErrorCode )
            {
                case UPLOAD_ERR_CANT_WRITE:
                    $this->ErrorMessage = \UF\_( '_io_upload',
                        'Uploading files isnt avialable cause there are no rights to write in temp upload folder.' );
                    break;
                case UPLOAD_ERR_EXTENSION:
                    $this->ErrorMessage = \UF\_( '_io_upload',
                        'Uploading files fails because files with defined extension are be accepted.' );
                    break;
                case UPLOAD_ERR_INI_SIZE:
                    $this->ErrorMessage = \UF\_( '_io_upload',
                        'Uploading files fails because the uploaded file is larger than allowed (php.ini)' );
                    break;
                case UPLOAD_ERR_NO_TMP_DIR:
                    $this->ErrorMessage = \UF\_( '_io_upload',
                        'Uploading files fails because there is no usable upload folder defined!' );
                    break;
                case UPLOAD_ERR_PARTIAL:
                    $this->ErrorMessage = \UF\_( '_io_upload',
                        'Uploading files fails because the upload isnt finishied yet!' );
                    break;
                default:
                    if (!empty($fieldname))
                    {
                        if ( !\is_uploaded_file($uploadData['tmp_name']) )
                        {
                            $this->ErrorMessage = \UF\_( '_io_upload',
                                'Uploading files fails because the uploaded file is invalid!' );
                            $this->ErrorCode = self::ERROR_INVALID_SOURCE;
                        }
                        elseif ($this->Size > $this->MaxFileSize)
                        {
                            $this->ErrorMessage = \UF\_2(
                                'Uploading files fails because the uploaded file size %d bytes is larger than %d bytes!',
                                $this->Size, $this->MaxFileSize );
                            $this->ErrorCode = self::ERROR_BAD_SIZE_BY_CONFIG;
                        }
                        else $this->ErrorMessage = null;
                    }
                    else $this->ErrorMessage = null;
                    break;
            }
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P U B L I C   M E T H O D S   - - - - - - - - - - - - - - - - - - - - -">

        /**
         * Returns if currently a upload exists (valid or invalid)
         *
         * @return boolean
         * @since  v0.1
         */
        public final function isUpload()
        {
            return ($this->ErrorCode != self::ERROR_NO_UPLOAD);
        }

        /**
         * Returns if current existing upload has a error.
         *
         * @since  v0.1
         */
        public final function hasError()
        {
            return ($this->ErrorCode != self::ERROR_NO_UPLOAD &&
                    $this->ErrorCode != self::ERROR_NOERROR);
        }

        # </editor-fold>

        # <editor-fold defaultstate="collapsed" desc="- - - -   P R I V A T E   M E T H O D S   - - - - - - - - - - - - - - - - - - - -">

        /**
         * @since  v0.1
         */
        private function init1( array $uploadData, array $requiredExtensions )
        {
            if ( 4 <= \count( $uploadData ) && isset( $uploadData['name'] ) &&
                 isset( $uploadData['size'] ) && isset( $uploadData['tmp_name'] ) &&
                 isset( $uploadData['error'] ) ) return true;
            $this->TempFile = $this->Filename = $this->Mimetype = null;
            $this->Size = 0;
            $this->Date = \UF\DateTime::Now();
            $this->ErrorCode = self::ERROR_NO_UPLOAD;
            $this->ErrorMessage = $this->FieldName = null;
            $this->RequiredExtensions = $requiredExtensions;
            return false;
        }
        /**
         * @since  v0.1
         */
        private function init2( array $uploadData, array $requiredExtensions )
        {
            if ( $uploadData['error'] != \UPLOAD_ERR_FORM_SIZE &&
                 $uploadData['error'] != \UPLOAD_ERR_NO_FILE &&
                 9 < $this->Size )
                return true;
            $this->TempFile = null;
            $this->Filename = null;
            $this->Mimetype = null;
            $this->Size = 0;
            $this->Date = \UF\DateTime::Now();
            $this->ErrorCode = self::ERROR_NO_UPLOAD;
            $this->ErrorMessage = null;
            $this->FieldName = null;
            $this->RequiredExtensions = $requiredExtensions;
            return false;
        }
        /**
         * @since  v0.1
         */
        private function init3( $fieldname, array $uploadData, array $requiredExtensions, &$ext )
        {
            if ( 1 > \count( $requiredExtensions ) ) return true;
            $this->RequiredExtensions = $requiredExtensions;
            $ext = \strtolower( \ltrim( \UF\IO\File::GetExtension( $uploadData['name'] ),'.' ) );
            $exts = '~^(' . \join( '|', $requiredExtensions ) . ')$~i';
            if ( \preg_match( $exts, $ext ) ) return true;
            $this->FieldName = $fieldname;
            $this->TempFile  = $uploadData['tmp_name'];
            $this->Filename = \basename( $uploadData['name'] );
            $this->Mimetype = \UF\IO\MimetypeTool::GetByExtension( $ext );
            $this->Size = $uploadData['size'];
            $this->Date = new \UF\DateTime( \filemtime( $this->TempFile ) );
            $this->ErrorCode = self::ERROR_EXTENSION;
            $this->ErrorMessage = \UF\_('_io_upload', 'Uploading file fails cause it uses a unallowed extension!' );
            return false;
        }

        # </editor-fold>

    }

}

